/*
 * cpu_mem.cpp subversion ready
 * get information about current CPU and Memory usage
 * the main process logic come from the source codes of linux's "top".
 */

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <sys/statfs.h>

extern "C"
{
#include"../noid/lib.h"
}
#include "../tinyxml/cpuxml.h"
#include "../msgqueue/MsgQue.h"
#include "../utilize/timeout.h"
#include <iostream>
using namespace std;

#define PROC_SUPER_MAGIC 0x9fa0
#define PROCFS "/proc"

static long cp_time[4];
static long cp_old[4];
static long cp_diff[4];

static int cpu_states[4];
static int memory_stats[6];

unsigned char nid[16];
//static const CBUFFSIZE = 2048;

MsgQue msgQue("/", 0);

/*		struct to record system information, check it out using the following codes
 *		info is an entity of struct systemInfo
 * 		printf("CPU: Used:%.1f%%\t", (float) info.cpustates[0] / 10);
 printf("Nice:%.1f%%\t", (float) info.cpustates[1] / 10);
 printf("System:%.1f%%\t", (float) info.cpustates[2] / 10);
 printf("Idle:%.1f%%\n", (float) info.cpustates[3] / 10);

 printf("Memory: Total:%d\t", info.memory[0]);
 printf("Free:%d\t", info.memory[1]);
 printf("Buffers:%d\t", info.memory[2]);
 printf("Cached:%d\n", info.memory[3]);

 printf("Swap: Total:%d\t", info.memory[4]);
 printf("Free:%d\n", info.memory[5]);
 */

struct systemInfo
{
	double load_avg[3];
	int *procstates;
	int *cpustates;
	int *memory;
};

//void buildXml(struct system_info *info2);

//跳过前面一堆空格
static inline char *skip_ws(const char *p)
{
	while (isspace(*p))
		p++;
	return (char *) p;
}

//路过前面一堆空格和一个关键词
static inline char *skip_token(const char *p)
{
	while (isspace(*p))
		p++;
	while (*p && !isspace(*p))
		p++;
	return (char *) p;
}

/*
 *  percentages(cnt, out, new, old, diffs) - calculate percentage change
 *	between array "old" and "new", putting the percentages i "out".
 *	"cnt" is size of each array and "diffs" is used for scratch space.
 *	The array "old" is updated on each call.
 *	The routine assumes modulo arithmetic.  This function is especially
 *	useful on BSD mchines for calculating cpu state percentages.
 */

long percentages(int cnt, int *out, register long *new1, register long *old,
		long *diffs)
{
	register int i;
	register long change;
	register long total_change;
	register long *dp;
	long half_total;

	/* initialization */
	total_change = 0;
	dp = diffs;

	/* calculate changes for each state and the overall change */
	for (i = 0; i < cnt; i++)
	{
		if ((change = *new1 - *old) < 0)
		{
			/* this only happens when the counter wraps */
			change = (int) ((unsigned long) *new1 - (unsigned long) *old);
		}
		total_change += (*dp++ = change);
		*old++ = *new1++;
	}

	/* avoid divide by zero potential */
	if (total_change == 0)
	{
		total_change = 1;
	}

	/* calculate percentages based on overall change, rounding up */
	half_total = total_change / 2l;
	for (i = 0; i < cnt; i++)
	{
		*out++ = (int) ((*diffs++ * 1000 + half_total) / total_change);
	}

	/* return the total in case the caller wants to use it */
	return (total_change);
}

void get_system_info(struct systemInfo *info)
{
	char buffer[4096 + 1];
	int fd, len;
	char *p;

	/* make sure the proc filesystem is mounted */
	{
		struct statfs sb;
		if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
		{
			fprintf(stderr, "Proc filesystem not mounted on " PROCFS "\n");
			exit(-1);
		}
	}

	/* chdir to the proc filesystem to make things easier */
	chdir(PROCFS);

	/* get load averages */
	{
		fd = open("loadavg", O_RDONLY);
		len = read(fd, buffer, sizeof(buffer) - 1);
		close(fd);
		buffer[len] = '\0';

		info->load_avg[0] = strtod(buffer, &p);
		info->load_avg[1] = strtod(p, &p);
		info->load_avg[2] = strtod(p, &p);
	}

	/* get the cpu time info */
	{
		fd = open("stat", O_RDONLY);
		len = read(fd, buffer, sizeof(buffer) - 1);
		close(fd);
		buffer[len] = '\0';

		p = skip_token(buffer); /* "cpu" */
		cp_time[0] = strtoul(p, &p, 0);
		cp_time[1] = strtoul(p, &p, 0);
		cp_time[2] = strtoul(p, &p, 0);
		cp_time[3] = strtoul(p, &p, 0);

		/* convert cp_time counts to percentages */
		percentages(4, cpu_states, cp_time, cp_old, cp_diff);
	}

	/* get system wide memory usage */
	{
		char *p;

		fd = open("meminfo", O_RDONLY);
		len = read(fd, buffer, sizeof(buffer) - 1);
		close(fd);
		buffer[len] = '\0';

		/* be prepared for extra columns to appear be seeking
		 to ends of lines */
		p = buffer;
		p = skip_token(p);
		memory_stats[0] = strtoul(p, &p, 10); /* total memory */

		p = strchr(p, '\n');
		p = skip_token(p);
		memory_stats[1] = strtoul(p, &p, 10); /* free memory */

		p = strchr(p, '\n');
		p = skip_token(p);
		memory_stats[2] = strtoul(p, &p, 10); /* buffer memory */

		p = strchr(p, '\n');
		p = skip_token(p);
		memory_stats[3] = strtoul(p, &p, 10); /* cached memory */

		int i;
		for (i = 0; i < 8; i++)
		{
			p++;
			p = strchr(p, '\n');
		}

		p = skip_token(p);
		memory_stats[4] = strtoul(p, &p, 10); /* total swap */

		p = strchr(p, '\n');
		p = skip_token(p);
		memory_stats[5] = strtoul(p, &p, 10); /* free swap */
	}

	/* set arrays and strings */
	info->cpustates = cpu_states;
	info->memory = memory_stats;
}

void buildXml(struct systemInfo *info)
{
	//char strTmp[128];

//	char fifoName[32];
//	char buf[CBUFFSIZE];
//	int done = -1;
//	int tmp, fdread;
//	while (done) //总会找到一个可用的整数的
//	{
//		tmp = rand();
//		sprintf(fifoName, "/tmp/context%d.fifo", tmp);
//		done = mkfifo(fifoName, O_RDWR);
//	}

	char msg[MBUFSIZ];

	time_t curTime;
	time(&curTime);

	CPU_XML xml;
	xml.setTitle("ContextRequest");
	xml.setType("resourceInfo");
	xml.setOper("store");
	xml.setNID(nid);
	xml.setTime(curTime);

	//xml.setReqID(tmp);

	xml.setCPU((float) info->cpustates[0] / 10);
	xml.setMem(info->memory[1]);
	xml.formXML(msg);
	printf("Before send to msgque :\n %s", msg);

	//exit(0);

	msgQue.send(1, msg);

//	if ((fdread = open(fifoName, O_RDONLY | O_NONBLOCK, 0)) < 0)
//	{
//		printf("open error for reading\n");
//		return;
//	}
//
//	tmp = readReady(fdread, 0, 500000);
//	cout << "********** tmp: " << tmp << endl;
//
//	if (tmp)
//	{
//		int nread = read(fdread, buf, CBUFFSIZE);
//		printf("Received %ld bytes: %s\n", (long) nread, buf);
//	}
//
//	close(fdread);
//	unlink(fifoName);

	return;

	//    TiXmlDocument xmlDoc( "cpu_memory.xml" ); // 建立一个XML文件
	//    TiXmlDeclaration Declaration( "1.0","UTF-8", "yes" ); // 声明XML的属性
	//    xmlDoc.InsertEndChild( Declaration ); // 写入基本的XML头结构
	//    TiXmlNode * pNode = NULL;
	//    TiXmlElement* pRootElm = NULL;
	//    TiXmlElement* pChildElm1 = NULL;
	//    TiXmlElement* pChildElm2 = NULL;
	//    TiXmlText* pText = NULL; // 一个指向Text的指针
	//
	//    pRootElm = new TiXmlElement( "contextRequest" );
	//    pRootElm->SetAttribute("msgType", "resourceInfo") ;
	//    pNode = xmlDoc.InsertEndChild(*pRootElm);						// first level (root), xmlDoc insert it
	//
	//    pRootElm = pNode->ToElement();
	//
	//    pChildElm1 = new TiXmlElement( "operation" );
	//    pText = new TiXmlText("store") ;
	//    pChildElm1->InsertEndChild(*pText) ;									// child level 1
	//    pRootElm->InsertEndChild(*pChildElm1) ;
	//    delete pChildElm1 ;
	//    delete pText ;
	//
	//    pChildElm1 = new TiXmlElement("nodeId") ;
	//    pText = new TiXmlText("1111222233334444") ;
	//    pChildElm1->InsertEndChild(*pText) ;
	//    pRootElm->InsertEndChild(*pChildElm1) ;
	//    delete pText;
	//    delete pChildElm1 ;
	//
	//
	//    pChildElm1 = new TiXmlElement( "details" );
	//
	//    pNode = pRootElm->InsertEndChild(*pChildElm1) ;
	//    pRootElm = pNode->ToElement() ;
	//
	//    pChildElm2 = new TiXmlElement("time") ;				// child level 2
	//    sprintf(strTmp, "%d", (int)curTime);
	//    pText = new TiXmlText(strTmp) ;
	//    pChildElm2->InsertEndChild(*pText) ;
	//    pRootElm->InsertEndChild (*pChildElm2);
	//    delete pText;
	//    delete pChildElm2 ;
	//
	//    pChildElm2 = new TiXmlElement("cpu") ;
	//    sprintf(strTmp, "%.1f", (float) info->cpustates[0] / 10);
	//    pText = new TiXmlText(strTmp);
	//    pChildElm2->InsertEndChild(*pText) ;
	//    pRootElm->InsertEndChild (*pChildElm2);
	//    delete pText;
	//    delete pChildElm2 ;
	//
	//    pChildElm2 = new TiXmlElement("memory") ;
	//    sprintf(strTmp, "%d", info->memory[1]);
	//    pText = new TiXmlText(strTmp);
	//    pChildElm2->InsertEndChild(*pText) ;
	//    pRootElm->InsertEndChild (*pChildElm2);
	//    delete pText;
	//    delete pChildElm2 ;
	//
	//
	//	TiXmlPrinter printer;
	//	xmlDoc.Accept(&printer);
	//	int t = printer.Size() + 1; // the size of useful string
	//	char *a = (char *) malloc(t);
	//	strcpy(a, printer.CStr());
	//
	//	printf("%s \n", a);
}

void getResponse()
{
	char fifoName[32];
	int done = -1;
	int tmp;
	while (done) //总会找到一个可用的整数的
	{
		tmp = rand();
		sprintf(fifoName, "/tmp/context%d.fifo", tmp);
		done = mkfifo(fifoName, O_RDWR);
	}
}

int main()
{
	if (GetConfigValue("../test/config.ini", "HOST", "NODEID", nid, STRSTR) != 0)
	{
		perror("Failed to read NODEID from configuration file.\n");
		return -1;
	}
	srand(time(0));
	struct systemInfo info;
	for (;;) // loop! otherwise CPU usage info will be not correct
	{
		sleep(1);
		get_system_info(&info);
		buildXml(&info);

	}

	return 0;
}
